#include <map>
#include <set>
#include <string>
#include <vector>
#include <cstdlib>
#include <iomanip>
#include <fstream>
#include <iostream>
#include <assert.h>
#include <stdlib.h>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include <Types.h>
#include <Defines.h>
#include <Profiler.h>
#include <LoadMatrix.h>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void PrintSparseArrayToMtx( const map< U64, double > & spa, const string & fmt, const MatrixProperties & mtxMetaData, const string & fn ) {

   map< U64, double >::const_iterator it;

   const U32 nnz = spa.size();
   const U32 m = RoundUpToPow2( mtxMetaData.m );
   const U32 n = RoundUpToPow2( mtxMetaData.n );
   U32 r;
   U32 c;
   U64 p;
   double v;
   
   ofstream file( fn.c_str() );
   
   file << "%%MatrixMarket matrix coordinate real general" << endl;
   file << mtxMetaData.m << " " << mtxMetaData.n << " " << nnz << endl;
   
   for( it = spa.begin(); it != spa.end(); it++ ) {
      
      p = it->first;
      v = it->second;
      
      if     ( fmt == "PSA" ) { r = Pos2RowRMA( p, m, n ); c = Pos2ColRMA( p, m, n); }
      else if( fmt == "CSR" ) { r = Pos2RowRMA( p, m, n ); c = Pos2ColRMA( p, m, n); }
      else assert( false );

      file << r << " " << c << " " << v << endl;
   }
   file.close();
   
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void CountUnz( const std::string & mtxFileName, MatrixProperties & mtxMetaData ) {
   
   DEBUG_PRINT( "CountUnz()" );
   
   map< double, bool > unzmap;
   map< double, bool > unzmapOffDiag;
   
   ifstream inputFile( mtxFileName.c_str() );
   
   ProcessMMFileHeader( mtxFileName, inputFile, mtxMetaData );
      
   U64 r;
   U64 c;
   U64 nnz = 0ULL;
   U64 nnzOffDiag = 0ULL;
   U64 numExplicitZeros = 0ULL;
   double v;
   assert( !mtxMetaData.isComplex );
   assert( mtxMetaData.isReal || mtxMetaData.isInteger );

   string line;
   const char * scanfRealFmt = "%ld %ld %lf\n";
   
   while( getline( inputFile, line ) ) {
   
      sscanf( line.c_str(), scanfRealFmt, &r, &c, &v );
      if( v == 0.0 ) {
         numExplicitZeros++;
         continue;
      }
      nnz++;
      unzmap[ v ] = true;
      if( r != c ) {
         nnzOffDiag++;
         unzmapOffDiag[ v ] = true;
         if( mtxMetaData.isSymmetric ) {
            nnz++;
         }
      }
   }
   
   mtxMetaData.numExplicitZeros = numExplicitZeros;
   mtxMetaData.nnz = nnz;
   mtxMetaData.unz = unzmap.size();
   mtxMetaData.nnzOffDiag = nnzOffDiag;
   mtxMetaData.unzOffDiag = unzmapOffDiag.size();
}
void CountUnzWrapper( const string & mtxFileName ) {
   
   Profiler prof;
   
   MatrixProperties mtxMetaData;
   map< U64, double > spa;
   
   prof.Start( "CountUnz" );
   CountUnz( mtxFileName, mtxMetaData );
   prof.Stop();
   
   cout << mtxFileName << endl;
   cout << "nnz = " << mtxMetaData.nnz << endl;
   cout << "unz = " << mtxMetaData.unz << endl;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
int main( int argc, char ** argv ) {

   if( argc < 2 ) {
      cout << "usage: " << argv[0] << " <mtxFile>" << std::endl;
      exit( -1 );
   }
   
   string mtxFileName = argv[1];
   
   DEBUG_PRINT( "main(): unz" );
   
   CountUnzWrapper( mtxFileName );
         
   return 0;
}
